package srzy.diameter

import scala.annotation.tailrec
import srzy.common.packunpack

object BytesToNode extends srzy.common.Logging {
  import srzy.diameter.DataFormat._

  def dccBytesToNode(arr: Array[Byte]): scala.xml.Elem = {
    val l = arr.toList
    val header = l.slice(0, dccHeaderLength)
    val body = l.slice(dccHeaderLength, l.length)
    val dccHeaderInfo = dccHeaderFieldsInfo.map(x => {
      val e1 = x._1
      val e2 = x._2
      (e1, packunpack.unpackLong(header.toArray, e2._1, e2._2))
    })

    val dccName = dccHeaderDict.find(x => {
      val l = x._2.map(e => if (e._1 == "flags")
        (e._1, getRequestFlag(e._2.toLong))
      else
        (e._1, e._2.toLong))
      val dccHeaderInfoTmp = dccHeaderInfo.map(e => if (e._1 == "flags")
        (e._1, getRequestFlag(e._2.toLong))
      else
        (e._1, e._2.toLong))
      l.map(t => dccHeaderInfoTmp.exists(q => q == t)).forall(x => x == true)
    }) match {
      case Some(v) => v._1
      case None => {
        val dccHeaderInfoTmp = dccHeaderInfo.map(e => if (e._1 == "flags")
          (e._1, getRequestFlag(e._2.toLong))
        else
          (e._1, e._2.toLong))
        val dccHeaderDictTmp = dccHeaderDict.map(q => (q._1, q._2.map(e => if (e._1 == "flags")
          (e._1, getRequestFlag(e._2.toLong))
        else
          (e._1, e._2.toLong))))
        logger.error("dccHeaderInfoTmp:\n" + dccHeaderInfoTmp.mkString("\n"))
        logger.error("dccHeaderDictTmp:" + dccHeaderDictTmp.mkString("\n"))
        throw new Exception("dccHeaderDict not contain field " + dccHeaderInfo)
      }
    }
    <command name={ dccName }>{ avpsBytesToNode(body.toArray) }</command>

  }

  def splitAVPs(arr: Array[Byte]) = {
    def getAVPLength(arr: Array[Byte]) = {
      val lengthInfo = withOptionAVPHeaderFieldsInfoMap.get(avpHeaderLengthText).get
      packunpack.unpackLong(arr.toArray, lengthInfo._1, lengthInfo._2).toInt
    }
    def getAVPField(arr: Array[Byte]): (Array[Byte], Array[Byte]) = {
      arr.splitAt(padLength(getAVPLength(arr)))
    }
    @tailrec def f(l: List[Array[Byte]], arr: Array[Byte]): List[Array[Byte]] = {
      if (arr.length == 0) {
        l
      } else {
        val (a1, a2) = getAVPField(arr)
        f(l ::: a1 :: Nil, a2)
      }
    }
    f(List.empty[Array[Byte]], arr)
  }

  def avpsBytesToNode(arr: Array[Byte]): scala.xml.NodeSeq = {
    splitAVPs(arr).map(avpBytesToNode(_)).foldLeft(xml.NodeSeq.Empty)(_ ++ _)
  }

  def avpBytesToNode(arr: Array[Byte]): scala.xml.Elem = {
    val lengthInfo = withOptionAVPHeaderFieldsInfoMap.get(avpHeaderLengthText).get
    val length = packunpack.unpackLong(arr.toArray, lengthInfo._1, lengthInfo._2)

    val flagInfo = withOptionAVPHeaderFieldsInfoMap.get(option._4).get
    val flag = packunpack.unpackLong(arr.toArray, flagInfo._1, flagInfo._2)

    val optionFlag = { flag >= option._5.toLong } //true if with option

    val headerLength = if (optionFlag) {
      withOptionAVPHeaderLength
    } else {
      noOptionAVPHeaderLength
    }

    val body = arr.slice(headerLength, length.toInt)

    val avpHeaderFields = (if (optionFlag) {
      withOptionAVPHeaderFieldsInfo.map(x => {
        val name = x._1
        val offset = x._2._1
        val digit = x._2._2
        (name, packunpack.unpackLong(arr, offset, digit))
      })
    } else {
      noOptionAVPHeaderFieldsInfo.map(x => {
        val name = x._1
        val offset = x._2._1
        val digit = x._2._2
        (name, packunpack.unpackLong(arr, offset, digit))
      })
    }).filter(_._1 != avpHeaderLengthText)

    val avpCode = avpHeaderFields.toMap.apply(avpHeaderType)
    val (avpName, dataType) = avpDict.find(x => x._2.toLong == avpCode) match {
      case Some(v) => (v._1, v._3)
      case None =>
        throw new Exception("avpCode :" + avpCode + " not found " + "\n" +
          "optionFlag:" + optionFlag + "\n" +
          "body : \n" + body.toFormatStr)
    }

    val r2 = java.util.regex.Pattern.compile("""[\p{Print}]*""")
    val r1 = """[\p{Print}]*""".r
    try {
      dataType match {
        case "Int"         => <avp name={ avpName } value={ packunpack.unpackInt(body, 0).toString }></avp>
        case "Long"        => <avp name={ avpName } value={ packunpack.unpackLong(body, 0).toString }></avp>
        case "UnsignedInt" => <avp name={ avpName } value={ packunpack.unpackLong(Array.fill(4)(0.asInstanceOf[Byte]) ++ body ++ Nil, 0).toString }></avp>
        case "String" | "UTF8String" =>
          <avp name={ avpName } value={
            val s = new String(body)
            val m = r2.matcher(s)
            if (m.matches()) {
              s
            } else {
              "0x" + body.toHexString
            }
          }></avp>
        case "Grouped" => <avp name={ avpName }>{ avpsBytesToNode(body) }</avp>
      }
    } catch {
      case ex: Exception => logger.debug("arr:\n" + arr.toFormatStr); throw ex
    }

  }
}